/*
 *  input_listener.cpp
 *  wonderland-project
 *
 *  Created by Ben Nolan on 22/09/08.
 *  Copyright 2008 Nolan Consulting Limited. All rights reserved.
 *
 */


#include "input_listener.h"
#include "wonderland.h"
#include <CEGUI/CEGUISystem.h>
#include <CEGUI/CEGUIWindow.h>
#include <CEGUI/CEGUIWindowManager.h>
#include <OIS/OIS.h>
#include <Ogre.h>
#include <map>
#include <string>


using namespace Ogre;

CEGUI::MouseButton convertOISMouseButtonToCegui(int buttonID){
    switch (buttonID){
		case 0: return CEGUI::LeftButton;
		case 1: return CEGUI::RightButton;
		case 2:	return CEGUI::MiddleButton;
		case 3: return CEGUI::X1Button;
		default: return CEGUI::LeftButton;
    }
};

InputListener::InputListener(RenderWindow* win, Camera* cam, SceneManager *sceneMgr){
  scene_manager_ = sceneMgr;

	// Cargo culted from exampleframelistener
	using namespace OIS;
  
	LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
	ParamList pl;
	size_t windowHnd = 0;
	std::ostringstream windowHndStr;

	win->getCustomAttribute("WINDOW", &windowHnd);
	windowHndStr << windowHnd;
	pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
	
	mInputManager = InputManager::createInputSystem( pl );

	mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
	mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));

	/* End culting */

	gui_layer_visible_ = false;
	
  // continue rendering?
  mContinue = true;

  mMouse->setEventCallback(this);
  mKeyboard->setEventCallback(this);

  mTimerElapsed = 0;
	
	windowResized(win);
}

void InputListener::windowResized(RenderWindow* rw)
{
	unsigned int width, height, depth;
	int left, top;
	rw->getMetrics(width, height, depth, left, top);

	const OIS::MouseState &ms = mMouse->getMouseState();
	ms.width = width;
	ms.height = height;
}

bool InputListener::frameEnded(const FrameEvent &evt) {
  // Hax to try and lock the framerate at 40fps.
  // todo fixme.
	Real ttW = 1000.0 / 40.0 - 1000.0 * evt.timeSinceLastFrame; 
	
	// todo: crossplatform?
	#ifdef MACOS
	usleep(ttW * 1000); 
	#endif

	return true; 
}

bool InputListener::frameStarted(const FrameEvent &evt) {
    mKeyboard->capture();
    mMouse->capture();

    // APPLE-Q
    if ((mKeyboard->isKeyDown (OIS::KC_Q)) && (mKeyboard->isKeyDown (OIS::KC_LWIN))) {
      mContinue=false;
    }

      // ALT-F4
  	if ((mKeyboard->isKeyDown (OIS::KC_F4)) && (mKeyboard->isKeyDown (OIS::KC_LMENU))) {
	  	mContinue=false;
	  }

    keyRepeat(evt.timeSinceLastFrame);

    return mContinue;
}

//----------------------------------------------------------------//
bool InputListener::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
  //todo - fixme - there is a bug with mouse down / mouse up events on os x
  //  mousePressed is never fired.
	if(gui_layer_visible_){
    CEGUI::System::getSingleton().injectMouseButtonDown(convertOISMouseButtonToCegui(id));
    CEGUI::System::getSingleton().injectMouseButtonUp(convertOISMouseButtonToCegui(id));
  }
  
	return true;
}

//----------------------------------------------------------------//
bool InputListener::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	if(gui_layer_visible_){
    CEGUI::System::getSingleton().injectMouseButtonDown(convertOISMouseButtonToCegui(id));
  }

	return true;
}


// MouseListener
bool InputListener::mouseMoved( const OIS::MouseEvent &arg )
{
	if(gui_layer_visible_){
    CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
  }
  
	return true;
}

	/* hax */

    /*
    
    // Setup the ray scene query
	mCamera = scene_manager_->getCamera("PlayerCam");
	
    CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
    Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width), mousePos.d_y/float(arg.state.height));

	SceneNode *mCurrentObject; 
	
	RaySceneQuery *mRaySceneQuery;
 	mRaySceneQuery = scene_manager_->createRayQuery(Ray());
    mRaySceneQuery->setRay(mouseRay);
    mRaySceneQuery->setSortByDistance(true);

    // Execute query
    RaySceneQueryResult &result = mRaySceneQuery->execute();
    RaySceneQueryResult::iterator itr;

	for(itr = result.begin(); itr != result.end(); itr++){
//		std::cout << "node selected.." << itr->movable->getName() << std::endl;

	    if (itr->movable){
			mCurrentObject = itr->movable->getParentSceneNode();
			
//			std::cout << "node selected.." << mCurrentObject->getName() << std::endl;

			if(mCurrentObject->getName().find("guid")==0){
				mCurrentObject->showBoundingBox(true);
//				nodeSelected(mCurrentObject);
				
//				std::cout << "node selected..";

				NodeData *data = Ogre::any_cast<NodeData *>(mCurrentObject->getUserAny());

				if(data->observers.find("click") != data->observers.end()){
					viewer->context->evaluate("(" + data->observers["click"] + ")()");
				}

//				guiScriptingContext->evaluate(Ogre::any_cast<std::string>(mCurrentObject->getUserAny()));
//				guiScriptingContext->evaluate("touch(node, avatar)");
				
				return true;
			}
		}else if(itr->worldFragment){
			//Entity *ent;
			//mCurrentObject = scene_manager_->getRootSceneNode()->createChildSceneNode(String(name) + "Node", itr->worldFragment->singleIntersection);
		}
	}

*/

void InputListener::toggleGuiLayer(){
	gui_layer_visible_ = !gui_layer_visible_;
	
	if(gui_layer_visible_){
		CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
		CEGUI::MouseCursor::getSingleton().show(); 
		wmgr.getWindow((CEGUI::utf8*)"Root")->setAlpha(1.0);
	}else{
		CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
		CEGUI::MouseCursor::getSingleton().hide(); 
		wmgr.getWindow((CEGUI::utf8*)"Root")->setAlpha(0.25);
	}
}

void InputListener::keyRepeat(float elapsed){
	if (mKey == OIS::KC_UNASSIGNED) return;	
	
	mElapsed += elapsed;
	
	// Wait 300ms, repeat every 30ms
	if( (gui_layer_visible_) && ((mElapsed-0.3) / 0.03 > mRepeats) ){
		CEGUI::System::getSingleton().injectKeyDown(mKey);
		CEGUI::System::getSingleton().injectChar(mChar);
		
		mRepeats++;
	}
}

   // KeyListener
bool InputListener::keyPressed(const OIS::KeyEvent &e) {

	switch (e.key) {
		case OIS::KC_ESCAPE:
			toggleGuiLayer();
			break;
		default:
			break;
	}

	// Repeating keystrokes
	
	mKey = e.key;
	mChar = e.text;
	mElapsed = 0;
	mRepeats = 0;

	if(gui_layer_visible_){
		CEGUI::System::getSingleton().injectKeyDown(e.key);
		CEGUI::System::getSingleton().injectChar(e.text);
	}

  return mContinue;
}
   
bool InputListener::keyReleased(const OIS::KeyEvent &e) { 
	mKey = OIS::KC_UNASSIGNED;
	
	CEGUI::System::getSingleton().injectKeyUp( e.key );

  return true;
}
